#include "Object.h"
#include "GlobalDefine.h"
#include "vec3.h"
#include "result.h"
#include "Ray.h"
#include "material.h"
#include "KDTree.h"
#include "Utility.h"
#include <cmath>
#include <limits>
#include <iostream>

namespace mray{

	bool Sphere::Hit(Ray& ray, Result* pResult)const{
		float t = FLT_MAX;
		vec3 temp = ray.m_ori - m_pos;
		float a = ray.m_dir * ray.m_dir;
		float b = temp*ray.m_dir * 2.0f;
		float c = temp*temp - m_radius*m_radius;
		float d = b*b - 4.0f*a*c;
		//there are more than one collision point
		if(d > 0.0f){
			float e = sqrt(d);
			float denom = 2.0f*a;
			//the normal here is normalized
			t = (-b-e)/denom;
			if(t > kEpsilon){
				if(ray.m_mint > t){
					ray.m_mint = t;
					if(pResult){
						pResult->n = (temp + ray.m_dir*t) * m_invRadius;
						pResult->p = ray.m_ori + ray.m_dir*t;
						pResult->pMtl = m_pMtl;
						pResult->d = -ray.m_dir;
					}
				}
				return true;
			}
			//if double sides
			//light can't pass from inside
			//else light can pass
			if(m_dSides == false) return false;

			t = (-b+e)/denom;
			if(t > kEpsilon){
				if(ray.m_mint > t){
					ray.m_mint = t;
					if(pResult){
						pResult->n = (temp + ray.m_dir*t) * -m_invRadius;
						pResult->p = ray.m_ori + ray.m_dir*t;
						pResult->pMtl = m_pMtl;
						pResult->d = -ray.m_dir;
					}
				}
				return true;
			}
		}
		return false;
	}

	bool Sphere::GetAABB(AABB& box) const
{
		box.Reset();
		box.m_min = m_pos - vec3(m_radius, m_radius, m_radius);
		box.m_max = m_pos + vec3(m_radius, m_radius, m_radius);
		return true;
	}

	Sphere::Sphere(const vec3& pos, float radius, BaseMaterial* pMtl, bool dSides) :
		BaseObject(pMtl, dSides),
		m_pos(pos), m_radius(radius), m_invRadius(1.f/radius){}

	Rectangle::Rectangle(const vec3& pos, const vec3& look, const vec3& up, float halfw, float halfh, BaseMaterial* pMtl, bool dSides) :
		BaseObject(pMtl, dSides),
		m_pos(pos), m_look(look), m_up(up), m_halfw(halfw), m_halfh(halfh)
	{
		m_look.normalize();
		m_right = m_up % m_look;
		m_right.normalize();
		m_up = m_look % m_right;
		m_up.normalize();
	}

	bool Rectangle::Hit(Ray& ray, Result* pResult) const
	{
		float DN = ray.m_dir * m_look;
		//if not double sides, light can't pass from back
		if(false == m_dSides && DN > -kEpsilon) return false;

		float t = (m_pos - ray.m_ori) * m_look / DN;
		if(t <= kEpsilon) return false;
		vec3 p = ray.GetPoint(t);
		vec3 d = p - m_pos;
		float x = d * m_right;
		if(x < -m_halfw || x > m_halfw) return false;
		float y = d * m_up;
		if(y < -m_halfh || y > m_halfh) return false;
		if(ray.m_mint > t){
			ray.m_mint = t;
			if(pResult){
				pResult->p = p;
				pResult->n = DN>0.f ? -m_look : m_look;
				pResult->d = -ray.m_dir;
				pResult->pMtl = m_pMtl;
			}
		}
		return true;
	}

	bool Rectangle::GetAABB(AABB& box) const
{
		box.Reset();
		box &= m_right*m_halfw;
		box &= m_right*-m_halfw;
		box &= m_up*m_halfh;
		box &= m_up*-m_halfh;
		box.m_min += m_pos;
		box.m_max += m_pos;
		box.Fromat();
		return true;
	}

}
